package org.dromara.crawler.mongo.mongoservice.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.dtflys.forest.Forest;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.http.ForestRequestType;
import com.dtflys.forest.http.ForestResponse;
import com.mongodb.BasicDBObject;
import com.mongoplus.aggregate.Aggregate;
import com.mongoplus.aggregate.AggregateWrapper;
import com.mongoplus.aggregate.pipeline.Accumulators;
import com.mongoplus.conditions.interfaces.ConditionOperators;
import com.mongoplus.conditions.interfaces.Projection;
import com.mongoplus.conditions.query.LambdaQueryChainWrapper;
import com.mongoplus.mapping.TypeReference;
import com.mongoplus.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.bson.Document;
import org.dromara.crawler.config.AnalysisAlgorithmProperties;
import org.dromara.crawler.domain.vo.DataSource;
import org.dromara.crawler.domain.vo.ElementEventVo;
import org.dromara.crawler.domain.vo.ElementTargetVo;
import org.dromara.crawler.domain.vo.HotspotEventVo;
import org.dromara.crawler.mongo.mongodomain.*;
import org.dromara.crawler.mongo.mongoservice.ElementTargetService;
import org.dromara.crawler.mongo.mongoservice.EventEventEdgesService;
import org.dromara.crawler.mongo.mongoservice.HotspotEventService;
import org.dromara.crawler.mongo.mongoservice.ElementEventService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mongoplus.model.PageResult;
import org.dromara.common.mybatis.core.page.PageQuery;

import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@RequiredArgsConstructor
public class HotspotEventServiceImpl extends ServiceImpl<HotspotEvent> implements HotspotEventService {

    private final AnalysisAlgorithmProperties analysisAlgorithmProperties;
    private final ElementEventService elementEventService;
    private final EventEventEdgesService eventEdgesService;
    private final ElementTargetService elementTargetService;

    @Override
    public HotspotEvent queryById(String id) {
        return this.getById(id);
    }

    @Override
    public List<HotspotEvent> getAll() {
        return this.list();
    }


    @Override
    public List<HotspotEvent> getAllOrderByScoreAndTime(String searchKeywords) {
        List<HotspotEvent> resultList;
        if (searchKeywords == null || searchKeywords.trim().isEmpty()) {
            resultList = this.list();
        } else {
            resultList = this.lambdaQuery()
                .like(HotspotEvent::getSearchKeywords, searchKeywords)
                .list();
        }

        return resultList.stream()
            .sorted(Comparator.comparing(HotspotEvent::getScore, Comparator.nullsLast(Comparator.reverseOrder()))
                .thenComparing(hotspotEvent -> {
                    Document eventTime = hotspotEvent.getEventTime();
                    if (eventTime != null && eventTime.containsKey("value")) {
                        Object value = eventTime.get("value");
                        if (value instanceof List) {
                            List<?> timeList = (List<?>) value;
                            if (timeList.size() > 1) {
                                Object endTimeObj = timeList.get(1);
                                if (endTimeObj instanceof String) {
                                    try {
                                        return DateUtil.parse((String) endTimeObj, DatePattern.NORM_DATE_PATTERN);
                                    } catch (Exception e) {
                                    }
                                }
                            }
                        }
                    }
                    return null;
                }, Comparator.nullsLast(Comparator.reverseOrder())))
            .collect(Collectors.toList());
    }


    @Override
    public Object queryHotspotEventTree(String eventId) {

        if (eventId == null || eventId.trim().isEmpty()) {
            List<HotspotEvent> allHotspotEvents = this.list();
            List<Map<String, Object>> allEvents = allHotspotEvents.stream()
                .map(hotspot -> {
                    Map<String, Object> eventData = new HashMap<>();
                    eventData.put("eventId", hotspot.getEventId());
                    eventData.put("eventTitle", hotspot.getEventTitle());
                    eventData.put("score", hotspot.getScore());
                    eventData.put("newsCount", hotspot.getNewsCount());
                    eventData.put("eventTime", hotspot.getEventTime());
                    eventData.put("content", hotspot.getContent());
                    eventData.put("searchKeywords", hotspot.getSearchKeywords());
                    eventData.put("keywords", hotspot.getKeywords());


                    if (hotspot.getNewsIds() != null && !hotspot.getNewsIds().isEmpty()) {

                        List<ElementEvent> elementEvents = elementEventService.lambdaQuery()
                            .in(ElementEvent::getSourceId, hotspot.getNewsIds())
                            .ne(ElementEvent::getStatus, "-1")
                            .list();


                        List<Map<String, String>> relatedEvents = elementEvents.stream()
                            .map(event -> {
                                Map<String, String> relatedEventData = new HashMap<>();
                                relatedEventData.put("event_id", event.getEventId());
                                relatedEventData.put("event_name", event.getEventName());
                                relatedEventData.put("event_status", event.getStatus());
                                return relatedEventData;
                            })
                            .collect(Collectors.toList());
                        eventData.put("relatedEvents", relatedEvents);
                    } else {
                        eventData.put("relatedEvents", new java.util.ArrayList<>());
                    }

                    return eventData;
                })
                .collect(Collectors.toList());

            return allEvents;
        }


        LambdaQueryChainWrapper<HotspotEvent> hotspotWrapper = this.lambdaQuery();
        hotspotWrapper.eq(HotspotEvent::getEventId, eventId);
        HotspotEvent hotspotEvent = this.one(hotspotWrapper);

        if (hotspotEvent == null) {
            return null;
        }


        Map<String, Object> result = new HashMap<>();
        result.put("eventId", hotspotEvent.getEventId());
        result.put("eventTitle", hotspotEvent.getEventTitle());


        if (hotspotEvent.getNewsIds() != null && !hotspotEvent.getNewsIds().isEmpty()) {

            List<ElementEvent> elementEvents = elementEventService.lambdaQuery()
                .in(ElementEvent::getSourceId, hotspotEvent.getNewsIds())
                .list();

            List<Map<String, String>> relatedEvents = elementEvents.stream()
                .map(event -> {
                    Map<String, String> eventData = new HashMap<>();
                    eventData.put("event_id", event.getEventId());
                    eventData.put("event_name", event.getEventName());
                    return eventData;
                })
                .collect(Collectors.toList());
            result.put("relatedEvents", relatedEvents);
        } else {
            result.put("relatedEvents", new java.util.ArrayList<>());
        }

        return result;
    }

    @Override
    public List<HotspotEvent> queryHotspotList(String keyword) {
        LambdaQueryChainWrapper<HotspotEvent> wrapper = this.lambdaQuery();
        if (ObjectUtil.isNotEmpty(keyword)) {
            LambdaQueryChainWrapper<HotspotEvent> orWrapper = this.lambdaQuery();
            orWrapper.like(HotspotEvent::getEventTitle, keyword);
            orWrapper.like(HotspotEvent::getKeywords, keyword);
            wrapper.or(orWrapper);
        }
        return list(wrapper);
    }

    @Override
    public Map<String, Integer> getMonthStatistics(String keyword) {
        List<String> yearMonthList = new ArrayList<>();
        YearMonth thisMonth = YearMonth.now();
        for (int i = 11; i >= 0; i--) {
            yearMonthList.add(thisMonth.minusMonths(i).toString()); // e.g., "2024-08"
        }
        LocalDate today = LocalDate.now();
        LocalDate monthsAgo = YearMonth.from(today.minusMonths(11)).atDay(1);
        Date startDate = Date.from(monthsAgo.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date endDate = Date.from(today.withDayOfMonth(today.lengthOfMonth()).atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant());
        List<HotspotEvent> hotspotEventList = queryHotspotList(keyword);
        List<String> eventSourceIdList = new ArrayList<>();
        hotspotEventList.stream().forEach(hotspotEvent -> {
            eventSourceIdList.addAll(hotspotEvent.getNewsIds());
        });

        AggregateWrapper elementEventAggregate = new AggregateWrapper();
        LambdaQueryChainWrapper<ElementEvent> wrapper = elementEventService.lambdaQuery();
        wrapper.in(ElementEvent::getSourceId, eventSourceIdList);
        wrapper.lt(ElementEvent::getTimePositionMoment, endDate);
        wrapper.gte(ElementEvent::getTimePositionMoment, startDate);
        elementEventAggregate.match(wrapper);
        elementEventAggregate.group(ConditionOperators.dateToString("%Y-%m", "$time_position_moment"), Accumulators.sum());
        elementEventAggregate.sortAsc("_id");
        List<Document> documentList = execute("element_event", elementEventAggregate, Document.class);
        Map<String, Integer> elementEventMap = documentList.stream()
            .collect(Collectors.toMap(
                d -> d.getString("_id"),
                d -> d.getInteger("count", d.getInteger("sum", 0))
            ));
        return elementEventMap;
    }

    @Override
    public Page<HotspotEvent> queryHotspotEventPage(String keyword, PageQuery pageQuery) {
        if (pageQuery.getPageNum() == null || pageQuery.getPageNum() <= 0) {
            pageQuery.setPageNum(1);
        }
        if (pageQuery.getPageSize() == null || pageQuery.getPageSize() <= 0) {
            pageQuery.setPageSize(10);
        }

        Page<HotspotEvent> page = pageQuery.build();
        LambdaQueryChainWrapper<HotspotEvent> wrapper = this.lambdaQuery();

        if (ObjectUtil.isNotEmpty(keyword)) {
            LambdaQueryChainWrapper<HotspotEvent> orWrapper = this.lambdaQuery();
            orWrapper.like(HotspotEvent::getId, keyword);
            orWrapper.like(HotspotEvent::getEventId, keyword);
            orWrapper.like(HotspotEvent::getEventTitle, keyword);
            orWrapper.like(HotspotEvent::getKeywords, keyword);
            orWrapper.like(HotspotEvent::getSearchKeywords, keyword);
            orWrapper.like(HotspotEvent::getEventTime, keyword);
            orWrapper.like(HotspotEvent::getReport, keyword);
            wrapper.or(orWrapper);
        }
        PageResult<HotspotEvent> pageResult = this.page(wrapper, pageQuery.getPageNum(), pageQuery.getPageSize());
        page.setRecords(pageResult.getContentData());
        page.setTotal(pageResult.getTotalSize());
        return page;
    }

    @Override
    public List<HotspotEventVo> getHotspotEvent() {
        AggregateWrapper hotspotMatch = new AggregateWrapper();
        hotspotMatch.project(Projection.builder()
            .display("event_id", "event_title", "score", "search_keywords", "news_ids")
            .buildList());
        List<HotspotEventVo> list = execute("hotspot_event", hotspotMatch, HotspotEventVo.class);
        List<String> eventSourceIdList = new ArrayList<>();
        list.stream().forEach(hotspotEvent -> {
            eventSourceIdList.addAll(hotspotEvent.getNewsIds());
        });
        AggregateWrapper elementEventAggregate = new AggregateWrapper();
        LambdaQueryChainWrapper<ElementEvent> wrapper = elementEventService.lambdaQuery();
        wrapper.in(ElementEvent::getSourceId, eventSourceIdList);
        elementEventAggregate.match(wrapper);
        elementEventAggregate.project(Projection.builder()
            .display("source_id", "event_id", "event_name", "time_position_moment")
            .buildList()).sortDesc("time_position_moment");
        List<ElementEventVo> eventList = execute("element_event", elementEventAggregate, ElementEventVo.class);
        list.stream().forEach(hotspotEvent -> {
            List<String> sourceIds = hotspotEvent.getNewsIds();
            List<ElementEventVo> elementEventVoList = eventList.stream().filter(elementEvent -> sourceIds.contains(elementEvent.getSourceDocId())).collect(Collectors.toList());
            hotspotEvent.setEventList(elementEventVoList);
        });
        return list;
    }

    @Override
    public List<ElementTargetVo> getRelatedTargets(String hotspotEventId) {
        AggregateWrapper hotspotMatch = new AggregateWrapper();
        hotspotMatch.project(Projection.builder()
            .display("event_id", "event_title", "score", "search_keywords", "news_ids")
            .buildList());
        List<HotspotEventVo> list = execute("hotspot_event", hotspotMatch, HotspotEventVo.class);
        List<String> eventSourceIdList = new ArrayList<>();
        list.stream().forEach(hotspotEvent -> {
            eventSourceIdList.addAll(hotspotEvent.getNewsIds());
        });
        AggregateWrapper elementEventAggregate = new AggregateWrapper();
        LambdaQueryChainWrapper<ElementEvent> wrapper = elementEventService.lambdaQuery();
        wrapper.in(ElementEvent::getSourceId, eventSourceIdList);
        elementEventAggregate.match(wrapper);
        elementEventAggregate.project(Projection.builder()
            .display("source_doc_id", "event_id", "event_name", "time_position_moment")
            .buildList()).sortDesc("time_position_moment");
        List<ElementEventVo> eventList = execute("element_event", elementEventAggregate, ElementEventVo.class);

        //查询边
        List<String> eventIds = eventList.stream().map(ElementEventVo::getEventId).collect(Collectors.toList());
        Map<String, List<EventEventEdges>> edgesMap = eventEdgesService.getRelation(eventIds);
        // ===== 从 edgesMap 里把所有参与过关系的 id 收集出来（包含事件和目标）=====
        Set<String> idsAppearedInEdges = edgesMap.values().stream()
            .filter(Objects::nonNull)
            .flatMap(List::stream)
            .flatMap(edge -> Stream.of(edge.getSrc(), edge.getDst()))
            .collect(Collectors.toCollection(LinkedHashSet::new));
        AggregateWrapper targetAggregate = new AggregateWrapper();
        LambdaQueryChainWrapper<ElementTarget> targetWrapper = elementTargetService.lambdaQuery();
        targetWrapper.in(ElementTarget::getTargetId, idsAppearedInEdges);
        targetAggregate.project(Projection.builder().display("_id","target_id", "name", "type").buildList());
        targetAggregate.match(targetWrapper).limit(10);
        List<ElementTargetVo> targetList = execute("element_target", targetAggregate, ElementTargetVo.class);
        if (targetList != null) {
            targetList.stream().forEach(item -> item.setId(item.get_id().toString()));
        }
        targetList.stream().forEach(elementTarget -> {
            ForestRequest forestRequest = Forest.request();
            Map<String, Object> map = Map.of("mongo_id", elementTarget.getId());
            forestRequest.addBody(map);
            forestRequest.setContentType("application/json");
            forestRequest.setType(ForestRequestType.POST);
            forestRequest.setUrl(analysisAlgorithmProperties.getHostUrl() + analysisAlgorithmProperties.getEventAnalyzePower());
            ForestResponse response = forestRequest.executeAsResponse();
            JSONObject jsonObject = JSONUtil.parseObj(response.getContent());
            elementTarget.setEvaluate(jsonObject.getStr("generated_result"));
        });
        return targetList;
    }

    private List<Map<String, Object>> execute(String collectionName, Aggregate<?> aggregate) {
        return baseMapper.aggregateList(collectionName, aggregate, new TypeReference<Map<String, Object>>() {
        });
    }


    private <T> List<T> execute(String collectionName, Aggregate<?> aggregate, Class<T> clazz) {
        return baseMapper.aggregateList(collectionName, aggregate, clazz);
    }

}

